# $Id: Section.pm,v 1.1 2004/02/21 18:48:07 ibo Exp $
package CGI::FOGE2::Section;

use CGI::FOGE2::Tools;
use strict;
our @ISA = ();
our $nbtables = 0; # used for tables that aren't named explicitly

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my %args = @_;

    my $self  = 
    {
        title  => undef,
        # the default value can be overridden
        table  => make_table_name(),
        # array of CGI::FOGE2::Question objects:
        questions => undef,
        prolog => undef,
        epilog => undef,
        # array of stats objects for the section:
        stats => [],
    };
    # mandatory fields
    my @mandatory_keys = ('title', 'table', # 'questions'
                          );

    # Write the hash values we received into $self
    foreach my $key (keys %args) {
        # Check that the hash we receive only has key names
        # we know about
        if (exists $self->{$key}) {
            $self->{$key} = $args{$key};
        } else {
            die(sprintf 'Section->new: '.msg->die_badkey, $key);
        }
    }

    # Private fields
    $self->{errmsg} = undef;

    # Check that all mandatory keys are present
    foreach my $key (@mandatory_keys) {
        if (!defined $self->{$key}) {
            die(sprintf 'Section->new: '.msg->die_missingkey, $key);
        }
    }

    bless ($self, $class);
    return $self;
}

sub addquestion {
    my $self = shift;
    push @{$self->{questions}}, @_;
}

sub make_table_name { # private, function
    # (!) call this function only once, in new
    # $nbtables is a package global
    return sprintf "table_%02d", $nbtables++;
}

sub get_table_name() {
    my $self = shift;
    return $self->{table};
}

sub serve {
    my $self = shift;
    my $has_prolog = defined $self->{prolog};
    my $has_epilog = defined $self->{epilog};

    my $out = '';
    $out .= CGI::h2({-class => "sectitle"}, $self->{title});
    $out .= CGI::span({-class => "sechead"},  $self->{prolog}) if $has_prolog;

    my $question_out = '';
    foreach my $question (@{$self->{questions}}) {
        $question_out .= $question->serve();
    }
    $out .= CGI::table($question_out);
    $out .= CGI::span({-class => "sechead"},  $self->{epilog}) if $has_epilog;

    return $out;
}

sub handle_submission {
    my $self = shift;

    my $table = $self->{table};
    my $request = sprintf "insert into %s(id%%s) values(?%%s);", $table;
    my ($fieldnames, $values, $nbfields) = ('', '', 0);

    # $fieldnames <- 'fieldname_1, fieldname_2, ..., fieldname_n'
    # $nbfields   <- n
    foreach my $question (@{$self->{questions}}) {
        # returns an array of IDs
        my @names = @{$question->generate_field_names()};
        $fieldnames .= ', ' . join ', ', @names;
        $nbfields += @names;
    }
    # $values = '?, ?, ..., ?' (there are n question marks)
    $values = ', ?' x $nbfields;

    # $request = 'insert into TABLE(fieldname_1, ...) VALUES(?, ...)'
    $request = sprintf $request, $fieldnames, $values;

    my $id = theform->unique_id();
    my $db = theform->dbh_w();
    my $sth = $db->prepare($request)
        || die "Cannot prepare insert request: " . DBI::errstr;

    # Package the sth into a Query so it may be easily passed to subclasses
    # of Question.
    my $STH = CGI::FOGE2::Query->new($sth, 0);

    my ($all_ok, $errmsg) = (1, '');

    # "placeholders are numbered from 1" (lifted from man DBI)
    $STH->add_param("$id");
    foreach my $q (@{$self->{questions}}) {
        # $q->handle_submission checks for errors and updates $sth
        # if everything is OK. In this case, i returns true. 
        # Otherwise, false is returned, and an error message is
        # stored. It can be retrieved with errormsg().
        my $ok= $q->handle_submission($STH);
        $all_ok &&= $ok;
        if (!$ok) {
            $errmsg .= CGI::li($q->errormsg());
        }
    }

    if (!$all_ok) {
        # print an error message and die
        # printf ... header ... $errmsg
        $self->store_error($errmsg);
        return 0;
    } else {
        # execute the query
        $sth->execute 
            || die(sprintf(msg->die_cantexec_insert1,$DBI::errstr));
        return 1;
    }
}

sub store_error { # private
    my $self = shift;
    $self->{errmsg} = shift;
}

sub errormsg {
    my $self = shift;
    return $self->{errmsg};
}

sub make_sql_structure {
    my $self = shift;
    my $fmt_create = "create table %s (\n%s\n);\n";
    my $fmt_column = "    %-32s %s";
    my $out = '';
    my @out = (sprintf $fmt_column, 'id', 'integer references %s');

    foreach my $question (@{$self->{questions}}) {
        foreach my $fieldname (@{$question->generate_field_names()}) {
            my $type = $question->ismandatory() ? 'text NOT NULL': 'text';
            push @out, sprintf $fmt_column, $fieldname, $type;
        }
    }
    $out = sprintf $fmt_create, $self->get_table_name, (join ",\n", @out);
    return $out;
}

sub get_sql_table_name {
    my $self = shift;
    return $self->{table};
}

sub getstats {
    my $self = shift;
    my $out = '';
    # First generate Section global stats
    foreach my $stat (@{$self->{stats}}) {
        $out .= $stat->getstats();
    }
    # Then generate stats for each question
    foreach my $question (@{$self->{questions}}) {
        $out .= $question->getstats();
    }
    $out = CGI::table($out);
    return $out;
}

sub finalize {
    my $self = shift;
    # Completes the initialization of some objects
    foreach my $question (@{$self->{questions}}) {
	foreach my $stat (@{$question->{stats}}) {
	    $stat->setrelation($self->{table});
            $stat->setquestion($question);
	}
    }
}

sub as_text {
    my $self = shift;
    my ($num, $indent, $shortformat) = @_;
    my $out = '';
    my $i = 0;

    $out .= sprintf "%s%s %s\n", $indent, list2str($num), $self->{title};

    if ($shortformat) {
        foreach my $question (@{$self->{questions}}) {
            $out .= $question->as_text_header([@$num, ++$i], $indent x 2);
        }
    } else {
        $out .= theform->stringwrap($self->{prolog}, $indent);
        foreach my $question (@{$self->{questions}}) {
            $out .= $question->as_text([@$num, ++$i], $indent x 2);
        }
        $out .= theform->stringwrap($self->{epilog}, $indent);
    }
    return $out;
}

1;
